trigger quoteTrigger on quote__c 
    (after insert, after update,
     before update, before delete) {

    if (Trigger.isBefore) {

		// Make sure we don't clear the primary bit
		if (Trigger.isUpdate) {
		
			// Note this idiom for working with parallel new and old arrays 
			for (Integer i = 0; i < Trigger.old.size(); i++) {
				Quote__c old = Trigger.old[i];
				Quote__c nw = Trigger.new[i];
				
			    if (old.primary__c && !nw.primary__c) {
			    	/* Unless we are in the middle of a transaction to switch to a 
			    	   different primary quote for this opportunity */
			        if (!util.opportunityIds.contains(old.opportunity__c)) {
						nw.Primary__c.addError('Primary quote cannot be marked non-primary');
					}
			    }
			}
		}
	    
    	// Make sure we don't delete the primary quote
        if (Trigger.isDelete) {
        	for (Quote__c q : Trigger.old) {
	            if (q.primary__c) {
	            	q.addError('Primary quote cannot be deleted');
	            }
	        }
        }
        
    } else {
    
        // When creating a new quote, copy the opp lineitems. 
    	if (Trigger.isInsert) util.copyLineItemsFromOpp(Trigger.new);
	
        /* After inserts and updates, we must ensure that only one record is marked
           as primary. To do so, we get all opportunities referenced by the quotes
           in Trigger.new that attempt to be primary and set the other's primary flags to false
           Special care is taken for the case where two or more records within this batch 
           attempt to be primary for the same opportunity */
        
        // used to hold quotes that are marked as primary
        List<Quote__c> primaries = new List<Quote__c>();

        //used to map the opportunity Ids for which there is a quote attempting to be primary
        //to the corresponding Quote
        Map<Id, Id> opWithPrimary = new Map<Id, Id>();
        
        // used to hold the Ids of quotes that attempt to be primary for the same opportunity
        Set<Id> badQuotes = new Set<Id>();

        for (Integer i = 0; i < Trigger.new.size(); i++) {
            Quote__c nw = Trigger.new[i];
            // If the new quote is marked as primary and was either just inserted
            // or was not the primary quote before this operation...
            if (nw.primary__c && (Trigger.isInsert || !Trigger.old[i].primary__c)) {
                // if there already is a quote that attempts to be primary for this quote's opportunity...
                if (opWithPrimary.containsKey(nw.opportunity__c)) {
                    // both quotes are bad, remember them
                    badQuotes.add(opWithPrimary.get(nw.opportunity__c));
                    badQuotes.add(nw.Id);                   
                } else {
                    // the quote is good so far, add it to our list
                    primaries.add(nw);
                    
                    // remember this quote's opportunity in case another quote also wants to be primary for it
                    opWithPrimary.put(nw.opportunity__c, nw.Id);
                }
            }
        }
        
        // mark the bad quotes as such
        for (Id bad : badQuotes) {
                Quote__c q = Trigger.newMap.get(bad);
            q.primary__c.AddError('More than one quote is attempting to be primary for this opportunity: ' + q.opportunity__c);
        }
        
        /* remove the bad quotes from our set of good ones
           we'll do this by first ensuring there are no duplicates by copying Ids into a set
           and then removing the bad quotes' Ids from that set */
        Set<Id> primaryIds = new Set<Id>();
        for (Quote__c q : primaries)
           primaryIds.add(q.Id);
           
        primaryIds.removeAll(badQuotes);        
        
        // now we have a set of unique and good quote ids, get the corresponding Quote__c objects
        primaries.clear();
        util.opportunityIds.clear();
        for (Id primId : primaryIds) {
            Quote__c q = Trigger.newMap.get(primId);
            primaries.add(q);
            /* remember the opportunity ids we're setting a new primary quote for across triggers
               so the before update trigger doesn't complain about there being no primary quote */
            util.opportunityIds.add(q.opportunity__c);
        }       
        
        /* The trigger makes a call to getOtherPrimaryQuotes() in the util package
           and assigns the result to a list of quotes. */
        Quote__c[] others = util.getOtherPrimaryQuotes(primaries);
        
        // Then it sets the primary flag to false for all the returned quotes and updates the list
        for (quote__c q : others) {
            q.primary__c = false;
        }
        
        try {
            update others;
        } finally {
            util.opportunityIds.clear();            
        }
    }
}